/**
 * @file 主进程状态管理器类
 * @description 提供主进程中的状态存储、持久化和同步功能
 * @author 黄敏
 * @created 2025-06-18 08:57
 * @updated 2025-01-27 优化注释和文档，添加版本控制功能
 */

import { app, webContents } from 'electron'
import { existsSync, mkdirSync, readFileSync, writeFile } from 'fs'
import { dirname, join } from 'path'
import { reactive, toRaw, watch } from '@vue/reactivity'
import type { Reactive, WatchCallback, WatchOptions } from '@vue/reactivity'
import debounce from 'lodash/debounce'
import isEqual from 'lodash/isEqual'
import { topic } from '../common/config'

/**
 * 字段变更类型枚举
 * @description 定义数据结构变更的类型
 */
enum FieldChangeType {
  /** 新增字段 */
  ADDED = 'added',
  /** 移除字段 */
  REMOVED = 'removed',
  /** 类型变更 */
  TYPE_CHANGED = 'type_changed'
}

/**
 * 字段变更信息接口
 * @description 描述字段变更的详细信息
 */
interface FieldChange {
  /** 字段名 */
  field: string
  /** 变更类型 */
  type: FieldChangeType
  /** 新值（用于新增或类型变更） */
  newValue?: unknown
  /** 旧值（用于移除或类型变更） */
  oldValue?: unknown
  /** 新类型 */
  newType?: string
  /** 旧类型 */
  oldType?: string
}

/**
 * 存储数据结构接口
 * @description 定义持久化存储的数据格式
 */
interface StorageData<T> {
  /** 数据版本号 */
  version: number
  /** 实际状态数据 */
  data: T
  /** 数据更新时间戳 */
  updatedAt: number
}

/**
 * Store配置选项接口
 * @description 定义状态存储器的配置参数
 */
interface StoreOptions {
  /** 最大重试次数，写入失败时的重试策略 */
  maxRetries?: number
  /** 重试延迟（毫秒），重试间隔时间 */
  retryDelay?: number
  /** 跳过默认值检查，允许相同名称的实例使用不同的默认值 */
  skipDefaultsCheck?: boolean
}

/**
 * 主进程状态管理器类
 * @description 提供状态存储、持久化、验证和监听功能
 * 支持自动保存、错误重试、状态验证和版本控制等特性
 *
 * @template T - 状态对象类型，必须继承自Record<string, unknown>
 *
 */
class CreateState<T extends Record<string, unknown>> {
  /** 静态实例映射，用于实现单例模式 */
  static instances = new Map<string, CreateState<Record<string, unknown>>>()
  /** 状态是否是全新创建 */
  readonly isNew:boolean = false

  /** 内部可写的响应式状态对象 */
  private readonly _state!: Reactive<T>

  /** 外部只读的响应式状态对象，支持Vue的响应式系统 */
  readonly state!: Readonly<T>

  /** 状态文件存储路径 */
  private readonly storagePath!: string

  /** 当前正在进行的写入操作Promise */
  private currentWritePromise: Promise<void> | null = null

  /** 最大重试次数 */
  private readonly maxRetries!: number

  /** 重试延迟（毫秒） */
  private readonly retryDelay!: number

  /**
   * 构造函数
   * @param name - 状态名称，用于标识和存储文件命名
   * @param defaults - 默认状态值
   * @param version - 状态版本号，用于数据迁移
   * @param options - 存储配置选项，包含验证器、重试配置和跳过默认值检查选项
   * @throws {Error} 当状态名称格式不正确时抛出错误
   * @throws {Error} 当相同名称的实例已存在但配置不匹配时抛出错误
   */
  constructor(
    private readonly name: string,
    private readonly defaults: T,
    private readonly version: number,
    options?: StoreOptions
  ) {
    // 验证状态名称格式，只允许字母、数字、下划线、中划线
    if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
      throw new Error('Store name can only contain letters, numbers, underscores and hyphens')
    }

    // 检查是否已存在相同名称的实例
    const existingInstance = CreateState.instances.get(name)
    if (existingInstance) {
      // 比较版本和默认值
      const isVersionMatch = existingInstance.version === version
      const isDefaultsMatch = isEqual(existingInstance.defaults, defaults)

      const skipDefaultsCheck = options?.skipDefaultsCheck || false

      if (isVersionMatch && (isDefaultsMatch || skipDefaultsCheck)) {
        // 版本匹配且（默认值匹配或跳过默认值检查），返回现有实例
        console.log(
          `[Store:${name}] Returning existing instance (v${version})${skipDefaultsCheck ? ' (skipped defaults check)' : ''}`
        )
        return existingInstance as CreateState<T>
      } else {
        // 版本或默认值不匹配，抛出错误
        const versionMsg = isVersionMatch
          ? 'version matches'
          : `version mismatch: existing v${existingInstance.version}, new v${version}`
        const defaultsMsg = skipDefaultsCheck
          ? 'defaults check skipped'
          : isDefaultsMatch
            ? 'defaults match'
            : 'defaults do not match'
        throw new Error(
          `Store with name '${name}' already exists but configuration does not match. ${versionMsg}, ${defaultsMsg}`
        )
      }
    }

    this.maxRetries = options?.maxRetries || 3
    this.retryDelay = options?.retryDelay || 1000
    this.version = version

    // 构建存储文件路径
    this.storagePath = join(app.getPath('userData'), `storage-state/${name}.json`)

    // 确保存储目录存在
    const storeDir = dirname(this.storagePath)
    if (!existsSync(storeDir)) {
      this.isNew = true
      mkdirSync(storeDir, { recursive: true })
    }

    // 加载初始状态
    const initialState = this.loadState()
    this._state = reactive(initialState)

    // 创建只读代理，外部无法修改
    // this.state = readonly(this._state) as Readonly<T>
    //在类型定义它为只读；让ts在运行时检查，不能使用readonly，因为它会在使用type-check命令时，变得很慢。
    this.state = this._state as Readonly<T>


    // 使用防抖处理状态保存，避免频繁写入
    const debouncedSave = debounce(() => {
      this.saveState()
    }, 300)

    // 监听状态变化，自动保存和通知监听器
    watch(
      this._state,
      () => {
        debouncedSave()
      },
      { deep: true }
    )

    // 将新实例添加到静态映射中
    CreateState.instances.set(name, this as CreateState<Record<string, unknown>>)
    console.log(`[存储:${this.name}] 新实例已创建 (版本${this.version})`)
  }

  /**
   * 监听指定键的状态变化
   * @param key - 要监听的键名
   * @param callback - 状态变化回调函数
   * @param options - 监听选项
   * @returns Vue的watch停止函数
   */
  watch<K extends keyof T>(
    key: K,
    callback: WatchCallback<T[K], T[K] | undefined>,
    options?: WatchOptions
  ) {
    return watch(() => (this._state as T)[key], callback, options)
  }

  /**
   * 设置状态值
   * @param keyOrValue - 键名或部分状态对象
   * @param value - 要设置的值（当第一个参数是键名时使用）
   */
  set<K extends keyof T>(keyOrValue: K | Partial<T>, value?: T[K]): void {
    let key: K | undefined = void 0
    if (typeof keyOrValue === 'object') {
      Object.entries(keyOrValue).forEach(([k, v]) => {
        ;(this._state as T)[k as K] = v as T[K]
      })
    } else {
      key = keyOrValue as K
      ;(this._state as T)[keyOrValue as K] = value as T[K]
    }

    // 通知所有渲染进程状态更新
    this.notifyRenderers(key)
  }

  // 销毁存储实例，
  destroy() {
    CreateState.instances.delete(this.name)
  }

  /**
   * 检测数据结构变化
   * @description 比较新旧数据结构，识别字段的增加、移除和类型变更
   * @param oldData - 旧数据
   * @param newDefaults - 新的默认值结构
   * @returns 字段变更信息数组
   *
   * @private
   */
  private detectChanges(oldData: Record<string, unknown>, newDefaults: T): FieldChange[] {
    const changes: FieldChange[] = []
    const oldKeys = new Set(Object.keys(oldData))
    const newKeys = new Set(Object.keys(newDefaults))

    // 检测新增字段
    for (const key of newKeys) {
      if (!oldKeys.has(key)) {
        changes.push({
          field: key,
          type: FieldChangeType.ADDED,
          newValue: newDefaults[key],
          newType: typeof newDefaults[key]
        })
      }
    }

    // 检测移除字段
    for (const key of oldKeys) {
      if (!newKeys.has(key)) {
        changes.push({
          field: key,
          type: FieldChangeType.REMOVED,
          oldValue: oldData[key],
          oldType: typeof oldData[key]
        })
      }
    }

    // 检测类型变更
    for (const key of oldKeys) {
      if (newKeys.has(key)) {
        const oldType = typeof oldData[key]
        const newType = typeof newDefaults[key]
        if (oldType !== newType) {
          changes.push({
            field: key,
            type: FieldChangeType.TYPE_CHANGED,
            oldValue: oldData[key],
            newValue: newDefaults[key],
            oldType,
            newType
          })
        }
      }
    }

    return changes
  }

  /**
   * 应用数据迁移
   * @description 根据检测到的变化自动迁移数据结构
   * @param oldData - 旧数据
   * @param oldVersion - 旧版本号
   * @param changes - 字段变更信息
   * @returns 迁移后的数据
   *
   * @private
   */
  private migrateData(
    oldData: Record<string, unknown>,
    oldVersion: number,
    changes: FieldChange[]
  ): T {
    // 自动迁移逻辑
    console.log(
      `[Store:${this.name}] Applying automatic migration from v${oldVersion} to v${this.version}`
    )
    const migratedData = { ...oldData }

    for (const change of changes) {
      switch (change.type) {
        case FieldChangeType.ADDED:
          // 添加新字段，使用默认值
          migratedData[change.field] = change.newValue
          console.log(
            `[Store:${this.name}] Added field '${change.field}' with default value:`,
            change.newValue
          )
          break

        case FieldChangeType.REMOVED:
          // 移除旧字段
          delete migratedData[change.field]
          console.log(`[Store:${this.name}] Removed field '${change.field}'`)
          break

        case FieldChangeType.TYPE_CHANGED:
          // 类型变更，使用默认值替换
          migratedData[change.field] = change.newValue
          console.log(
            `[Store:${this.name}] Changed field '${change.field}' type from ${change.oldType} to ${change.newType}`
          )
          break
      }
    }

    return migratedData as T
  }
  /**
   * 从存储文件加载状态
   * @description 如果文件不存在或读取失败，返回默认状态。支持版本控制和数据迁移
   * @returns 加载的状态对象
   *
   * @private
   */
  private loadState(): T {
    const defaults = JSON.parse(JSON.stringify(this.defaults)) as T
    if (existsSync(this.storagePath)) {
      try {
        const fileContent = readFileSync(this.storagePath, 'utf-8')
        let parsedData: StorageData<T> | T

        try {
          parsedData = JSON.parse(fileContent)
        } catch (parseError) {
          console.error(`[Store:${this.name}] Failed to parse JSON:`, parseError)
          return defaults as T
        }

        const storageData = parsedData as StorageData<T>

        if (storageData.version === this.version) {
          // 版本匹配，直接使用数据
          const mergedState = { ...defaults, ...storageData.data }
          console.log(`[Store:${this.name}] State loaded successfully (v${this.version})`)
          return mergedState
        } else {
          // 版本不匹配，需要迁移
          console.log(
            `[Store:${this.name}] Version mismatch: stored v${storageData.version}, current v${this.version}`
          )
          const changes = this.detectChanges(storageData.data, defaults)

          if (changes.length > 0) {
            console.log(
              `[Store:${this.name}] Detected ${changes.length} schema changes:`,
              changes.map(c => `${c.field}(${c.type})`).join(', ')
            )
          }

          const migratedData = this.migrateData(storageData.data, storageData.version, changes)
          console.log(
            `[Store:${this.name}] Data migrated from v${storageData.version} to v${this.version}`
          )
          return migratedData
        }
      } catch (error) {
        console.error(`[Store:${this.name}] Failed to load state:`, error)
        return defaults
      }
    }
    console.log(`[Store:${this.name}] No existing state file, using defaults (v${this.version})`)
    return defaults
  }

  /**
   * 执行写入操作，带重试机制
   * @description 当写入失败时自动重试，提高数据持久化的可靠性
   * @param data - 要写入的JSON字符串
   * @returns Promise<void>
   * @throws {Error} 当所有重试都失败时抛出错误
   *
   * @private
   */
  private async writeWithRetry(data: string): Promise<void> {
    let retries = 0
    let lastError: Error | null = null

    while (retries < this.maxRetries) {
      try {
        await new Promise<void>((resolve, reject) => {
          writeFile(this.storagePath, data, error => {
            if (error) {
              reject(error)
            } else {
              resolve()
            }
          })
        })
        return // 写入成功，直接返回
      } catch (error) {
        lastError = error as Error
        retries++
        if (retries < this.maxRetries) {
          console.warn(
            `[Store:${this.name}] Write failed, retrying (${retries}/${this.maxRetries})...`
          )
          await new Promise(resolve => setTimeout(resolve, this.retryDelay))
        }
      }
    }

    // 所有重试都失败了
    throw new Error(
      `[Store:${this.name}] Write failed after ${this.maxRetries} retries: ${lastError?.message}`
    )
  }

  /**
   * 将当前状态保存到存储文件
   * @description 使用新的存储格式，包含版本信息和时间戳
   *
   * @private
   */
  private async saveState(): Promise<void> {
    const storageData: StorageData<T> = {
      version: this.version,
      data: this._state as T,
      updatedAt: Date.now()
    }

    // 创建新的写入操作
    const writePromise = this.writeWithRetry(JSON.stringify(storageData, null, 2))

    // 如果已经有正在进行的写入操作，等待它完成
    if (this.currentWritePromise) {
      try {
        await this.currentWritePromise
      } catch (error) {
        // 忽略旧写入操作的错误
        console.warn(`[Store:${this.name}] Previous write operation failed:`, error)
      }
    }

    // 更新当前写入操作
    this.currentWritePromise = writePromise

    try {
      await writePromise
      console.log(`[Store:${this.name}] State saved successfully (v${this.version})`)
    } catch (error) {
      console.error(`[Store:${this.name}] Failed to save state:`, error)
      // 写入失败不影响内存中的状态
    } finally {
      // 如果当前写入操作已经完成，清除引用
      if (this.currentWritePromise === writePromise) {
        this.currentWritePromise = null
      }
    }
  }

  /**
   * 已注册的渲染进程ID集合
   * @description 跟踪所有需要接收状态更新的渲染进程
   */
  registeredRendererIds = new Set<number>()

  /**
   * 渲染进程获取状态数据
   * @param senderId - 发送者ID
   * */
  getByRenderer(senderId: number) {
    try {
      // 记录请求的渲染进程ID，用于后续状态更新通知
      this.registeredRendererIds.add(senderId)

      // 返回完整状态对象
      return toRaw(this._state)
    } catch (error) {
      console.error(`[GlobalState:${this.name}] Failed to get state:`, error)
      return null
    }
  }

  setByRenderer(name: string, data: T | T[keyof T], key?: keyof T) {
    console.log(`[GlobalState:${name}] Received state update from renderer:`, data,key)
    try {
      if (key) {
        // 设置单个键值
        this.set(key, data as T[keyof T])
      } else {
        // 设置完整状态
        const typedData = data as T
        this.set(typedData)
      }
    } catch (error) {
      console.error(`[GlobalState:${name}] Failed to set state:`, error)
    }
  }

  /**
   * 通知渲染进程状态更新
   * @description 使用防抖机制向所有注册的渲染进程广播状态变更
   *
   * @param key - 可选的特定键名，用于精确更新通知
   */
  notifyRenderers = debounce((key?: keyof T) => {
    this.registeredRendererIds.forEach(id => {
      try {
        const contents = webContents.fromId(id)
        if (contents && !contents.isDestroyed()) {
          if (key) {
            // 发送特定键的更新通知
            contents.send(`${topic.update}-${this.name}-${String(key)}`, toRaw((this._state as T)[key]))
          }
          // 发送完整状态更新通知
          contents.send(`${topic.update}-${this.name}`, toRaw(this._state))
        } else {
          // 移除已销毁的渲染进程
          this.registeredRendererIds.delete(id)
        }
      } catch (error) {
        console.error(`[全局状态:${this.name}] 通知渲染进程 ${id} 失败:`, error)
        this.registeredRendererIds.delete(id)
      }
    })
  }, 100)
}

export { CreateState, type StoreOptions, type FieldChange, FieldChangeType }
